package data;

import constants.Colors;
import constants.Difficulties;
import constants.Races;
import constants.TileTypes;

import java.util.ArrayList;
import java.util.Random;

/**
 * Map class to hold all information about the game and control the game
 *
 * @author Jared Moore
 * @date 10/5/13
 */
public class Map
{
	private Difficulties difficulty;
	private final Player[] players;
	private boolean isRandom;
	private final ArrayList<Colors> availableColors;
	private final Tile[][] map = new Tile[5][9];
	private int currentRound = 1, currentPlayer = 0;

	//food needed for any given round is equal to (currentRound - 1) / 4 + 3
	//TODO:  implement that somewhere

	/**
	 * Constructor
	 * Sets up the map with the given information
	 *
	 * @param difficulty The difficulty selected by the user
	 * @param isRandom True if the user wants a randomly generated map
	 * @param numOfPlayers The number of players in the game
	 */
	public Map(Difficulties difficulty, boolean isRandom, int numOfPlayers)
	{
		this.difficulty = difficulty;
		this.isRandom = isRandom;

		availableColors = new ArrayList<>(Colors.values().length);
		for (Colors i : Colors.values())
		{
			availableColors.add(i);
		}

		players = new Player[numOfPlayers];


		if (isRandom)
		{
			Random random = new Random();
			for (int i = 0; i < map.length; i++)
			{
				for (int j = 0; j < map[i].length; j++)
				{
					int value = random.nextInt(5);
					switch (value)
					{
						case 4:
							map[i][j] = new Tile(TileTypes.THREE_MOUNTAINS);
							break;
						case 3:
							map[i][j] = new Tile(TileTypes.TWO_MOUNTAINS);
							break;
						case 2:
							map[i][j] = new Tile(TileTypes.ONE_MOUNTAIN);
							break;
						case 1:
							map[i][j] = new Tile(TileTypes.RIVER);
							break;
						default:
							map[i][j] = new Tile(TileTypes.PLAIN);
					}
				}
			}

			int i = random.nextInt(5), j = random.nextInt(9);
			map[i][j] = new Tile(TileTypes.TOWN);

		}
		else
		{
			map[0][0] = new Tile(TileTypes.PLAIN);
			map[0][1] = new Tile(TileTypes.PLAIN);
			map[0][2] = new Tile(TileTypes.ONE_MOUNTAIN);
			map[0][3] = new Tile(TileTypes.PLAIN);
			map[0][4] = new Tile(TileTypes.RIVER);
			map[0][5] = new Tile(TileTypes.PLAIN);
			map[0][6] = new Tile(TileTypes.THREE_MOUNTAINS);
			map[0][7] = new Tile(TileTypes.PLAIN);
			map[0][8] = new Tile(TileTypes.PLAIN);

			map[1][0] = new Tile(TileTypes.PLAIN);
			map[1][1] = new Tile(TileTypes.ONE_MOUNTAIN);
			map[1][2] = new Tile(TileTypes.PLAIN);
			map[1][3] = new Tile(TileTypes.PLAIN);
			map[1][4] = new Tile(TileTypes.RIVER);
			map[1][5] = new Tile(TileTypes.PLAIN);
			map[1][6] = new Tile(TileTypes.PLAIN);
			map[1][7] = new Tile(TileTypes.PLAIN);
			map[1][8] = new Tile(TileTypes.ONE_MOUNTAIN);

			map[2][0] = new Tile(TileTypes.THREE_MOUNTAINS);
			map[2][1] = new Tile(TileTypes.PLAIN);
			map[2][2] = new Tile(TileTypes.PLAIN);
			map[2][3] = new Tile(TileTypes.PLAIN);
			map[2][4] = new Tile(TileTypes.TOWN);
			map[2][5] = new Tile(TileTypes.PLAIN);
			map[2][6] = new Tile(TileTypes.PLAIN);
			map[2][7] = new Tile(TileTypes.PLAIN);
			map[2][8] = new Tile(TileTypes.ONE_MOUNTAIN);

			map[3][0] = new Tile(TileTypes.PLAIN);
			map[3][1] = new Tile(TileTypes.TWO_MOUNTAINS);
			map[3][2] = new Tile(TileTypes.PLAIN);
			map[3][3] = new Tile(TileTypes.PLAIN);
			map[3][4] = new Tile(TileTypes.RIVER);
			map[3][5] = new Tile(TileTypes.PLAIN);
			map[3][6] = new Tile(TileTypes.TWO_MOUNTAINS);
			map[3][7] = new Tile(TileTypes.PLAIN);
			map[3][8] = new Tile(TileTypes.PLAIN);

			map[4][0] = new Tile(TileTypes.PLAIN);
			map[4][1] = new Tile(TileTypes.PLAIN);
			map[4][2] = new Tile(TileTypes.TWO_MOUNTAINS);
			map[4][3] = new Tile(TileTypes.PLAIN);
			map[4][4] = new Tile(TileTypes.RIVER);
			map[4][5] = new Tile(TileTypes.PLAIN);
			map[4][6] = new Tile(TileTypes.PLAIN);
			map[4][7] = new Tile(TileTypes.PLAIN);
			map[4][8] = new Tile(TileTypes.TWO_MOUNTAINS);
		}
	}

	/**
	 * Builds a player based on the given information
	 * @param name The name of the player
	 * @param race The race of the player
	 * @param color The color of the player
	 *
	 * @return Whether or not the player was set
	 *
	 * @precondition All of the players have not been set up yet
	 */
	public boolean buildPlayer(String name, Races race, Colors color)
	{
		for (int i = 0; i < players.length; i++)
		{
			if (players[i] == null)
			{
				int[] resources = new int[3];
				switch (difficulty)
				{
					case BEGINNER:
						resources[0] = 8;
						resources[1] = 4;
						break;
					default:
						resources[0] = 4;
						resources[1] = 2;
						break;
				}
                players[i] = new Player(name, race, color, resources, (i == 0) ? true : false);
				availableColors.remove(color);
				if (i + 1 < players.length)
				{
					return true;
				}
				else
				{
					break;
				}
			}
		}
		return false;
	}

	/**
	 * Gets the available colors so two players cannot have the same color
	 * @return An ArrayList with the colors that have not been claimed in it
	 */
	public ArrayList<Colors> getAvailableColors()
	{
		return availableColors;
	}

	public int getCurrentRound()
	{
		return currentRound;
	}

	public Tile[][] getMap()
	{
		return map;
	}

	public Player getCurrentPlayer()
	{
		return players[currentPlayer];
	}

    public int getTurnLength(Player myTurn)
    {
        if (myTurn.getFood() > 0)
        {
            return 50000;
        }
        else if (myTurn.getFood() == 0)
        {
            return 30000;
        }
        else
        {
            myTurn.addFood(myTurn.getFood() * -1);
            return 5000;
        }
    }

    public void nextTurn()
    {
        if (currentPlayer == players.length - 1)
        {
            currentPlayer = 0;
            currentRound++;
            decrementFood();
        }
        else
        {
            currentPlayer++;
        }
    }

    private void decrementFood()
    {
        int amount =  (currentRound - 1) / 4 + 3;
        for (Player i : players)
        {
            i.decrementFood(amount);
        }
    }

    public Player[] getPlayers(){
        return players;
    }
}